package predatorpreysystems;
/*
 * Copyright (c) 2009 Project E7
 *
 * This file is part of PredatorPreySystems
 *
 * PredatorPreySystems is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PredatorPreySystems is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PredatorPreySystems. If not, see <http://www.gnu.org/licenses/>.
 *
 * PredatorPreySystems is a project that demonstrates lotka volterra's predator
 * prey systems.  The project is also meant to demonstrate linear approximation
 * using differential equations and Euler's method.
 **/

import info.gridworld.actor.Actor;
import info.gridworld.grid.Grid;
import info.gridworld.grid.Location;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author markvismonte
 */
public class PredatorPreySystemUtility extends Object
    {
    //Data Members
    private Grid<Actor> grid;
    private FileOutputStream writer;
    private PrintStream outStream;
    private int predatorPopulation;
    private int preyPopulation;
    private int preyGrowth;
    private int preyDecay;
    private int predatorGrowth;
    private int predatorDecay;
    private int[][] preyGridPopulation;
    private int counter;

    /**
     * Creates a PredatorPreySystemUtility
     * @param grid - the world's boundedgrid
     */
    public PredatorPreySystemUtility(Grid<Actor> grid)
        {
        super();
        if (grid == null)
            throw new IllegalArgumentException("Actor grid is null");
        this.grid = grid;
        counter = 0;
        try {
            writer = new FileOutputStream(new File("pps.txt"),false);
            outStream = new PrintStream(writer);
            outStream.print("");
            writer = new FileOutputStream(new File("pps.txt"), true);
            outStream = new PrintStream(writer);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        preyGridPopulation = new int[5][5];
        }

    /**
     * Resets all the population, growth, and decay variables
     */
    public void reset()
        {
        counter++;
        outStream.println(counter + "," +preyPopulation + "," +
                predatorPopulation + "");
        predatorDecay = predatorGrowth = preyDecay = preyGrowth =
                predatorPopulation = preyPopulation = 0;
        for (int i = 0; i < preyGridPopulation.length; i++)
            for (int x = 0; x < preyGridPopulation[i].length; x++)
                preyGridPopulation[i][x] = 0;
        }

    /**
     * Recounts the grid areas for predator's population count
     */
    public void recountPrey()
        {
        for (int i = 0; i < 5; i++)
            for (int j = 0; j < 5; j++)
                for (int x = i * 5; x < (i * 5) + 5; x++)
                    for (int y = j * 5; y < (j * 5) + 5; y++)
                        if (getGrid().get(new Location(x, y)) instanceof Prey)
                            preyGridPopulation[i][j]++;
        }

    /**
     * A method that is able to predict the population in future turns
     * @param getPreyPopulation - true returns future prey population
     * false returns future predator opulation
     * @param calculateConstants - true if it is the first iteration and the
     * constants need to be calculated
     * @param preyPopulation - the current preyPopulation
     * @param predatorPopulation - the current predatorPopulation
     * @param c1 - constant 1 for dR/dt = c1 * R - c2 * R * W
     * @param c2 - constant 2 for dR/dt = c1 * R - c2 * R * W
     * @param c3 - ccnstant 3 for dW/dt = c3 * R * W - c4 * W
     * @param c4 - constant 4 for dW/dt = c3 * R * W - c4 * W
     * @param stepSize - step size for this approximation
     * @param stepSum - current amount of total step gap moved
     * @param finalStepGoal - total goal of stepping
     * @return preypopulation or predator population depending on
     * getPredyPopulation
     */
    public int predictFuturePopulation(boolean getPreyPopulation,
        boolean calculateConstants, double preyPopulation, 
        double predatorPopulation, double c1, double c2, double c3,double c4,
        double stepSize, double stepSum, double finalStepGoal)
        {
        if (calculateConstants)
            {
            calculateConstants = false;
            c1 = getPreyGrowth()/preyPopulation;
            c2 = getPreyDecay()/(preyPopulation * predatorPopulation);
            c3 = getPredatorGrowth()/(preyPopulation * predatorPopulation);
            c4 = getPredatorDecay()/(predatorPopulation);
            }
        double newPreyPopulation = preyPopulation + (((c1 * preyPopulation)
                - (c2 * predatorPopulation * preyPopulation)) * stepSize);
        double newPredatorPopulation = predatorPopulation + (((c3 * preyPopulation
                * predatorPopulation) - (c4 * predatorPopulation)) * stepSize);
        stepSum += stepSize;
        return stepSum >= finalStepGoal? (getPreyPopulation? (int)newPreyPopulation:
            (int)newPredatorPopulation): predictFuturePopulation(getPreyPopulation,
            calculateConstants,newPreyPopulation, newPredatorPopulation, c1, c2,
            c3, c4, stepSize, stepSum, finalStepGoal);
        }

    /**
     * A method which checks the populations areas with respect to the population
     * area of the given location
     * @param loc - the location which shows where the population are is
     * @return the location towards the food
     */
    public Location locationTowardsFood(Location loc)
        {
        int populationCounter = 0, posX = 0, posY = 0;
        int x = loc.getCol()/5, y = loc.getRow()/5;
        if (x - 1 >= 0)
            {
            populationCounter = Math.max(populationCounter, preyGridPopulation
                    [x - 1][y]);
            if (populationCounter == preyGridPopulation[x - 1][y])
                {
                posX = x - 1;
                posY = y;
                }
            }
        if (y - 1 >= 0)
            {
            populationCounter = Math.max(populationCounter, preyGridPopulation
                    [x][y - 1]);
            if (populationCounter == preyGridPopulation[x][y - 1])
                {
                posX = x;
                posY = y - 1;
                }
            }
        if (x + 1 < 5)
            {
            populationCounter = Math.max(populationCounter, preyGridPopulation
                    [x + 1][y]);
            if (populationCounter == preyGridPopulation[x + 1][y])
                {
                posX = x + 1;
                posY = y;
                }
            }
        if (y + 1 < 5)
            {
            populationCounter = Math.max(populationCounter, preyGridPopulation
                    [x][y + 1]);
            if (populationCounter == preyGridPopulation[x][y + 1])
                {
                posY = y + 1;
                posX = x;
                }
            }
        return new Location((posY * 5) + 2, (posX * 5) + 2);
        }

    /**
     * Prints out the prey populations on the grid
     */
    public void printPreyGrid()
        {
        for (int i = 0; i < preyGridPopulation.length; i++)
            System.out.println(Arrays.toString(preyGridPopulation[i]));
        }

    /**
     * Interface to increment predator population
     */
    public void incrementPredatorPopulation() {
        predatorPopulation++;
        }

    /**
     * Interface to increment prey population
     */
    public void incrementPreyPopulation() {
        preyPopulation++;
        }

    /**
     * Interface to increment prey growth
     */
    public void incrementPreyGrowth() {
        preyGrowth++;
        }

    /**
     * Interface to increment prey decay
     */
    public void incrementPreyDecay() {
        preyDecay++;
        }

    /**
     * Interface to increment predator growth
     */
    public void incrementPredatorGrowth() {
        predatorGrowth++;
        }

    /**
     * Interface to increment predator decay
     */
    public void incrementPredatorDecay() {
        predatorDecay++;
        }

    /**
     * Getter for grid
     * @return - world's grid
     */
    public Grid<Actor> getGrid() {
        return grid;
        }

    public int getPredatorDecay() {
        return predatorDecay;
        }

    public int getPredatorGrowth() {
        return predatorGrowth;
        }

    public int getPredatorPopulation() {
        return predatorPopulation;
        }

    public int getPreyDecay() {
        return preyDecay;
        }

    public int getPreyGrowth() {
        return preyGrowth;
        }

    public int getPreyPopulation() {
        return preyPopulation;
        }

    }
